<template>
    <!-- 画布元素，用于绘制粒子效果 -->
    <canvas ref="canvas" class="particles-canvas"></canvas>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'

/**
 * 粒子类型配置对象
 * 包含四种粒子效果：雪花(snow)、雨滴(rain)、落叶(leaves)、蒲公英(dandelion)
 * 每种类型包含：
 * - name: 显示名称
 * - color: 粒子颜色
 * - sizeRange: 粒子大小范围[min, max]
 * - speedRange: 下落速度范围[min, max]
 * - behavior: 初始化粒子特殊行为的函数
 * - update: 更新粒子特殊行为的函数
 * - draw: 绘制粒子形状的函数
 */
const PARTICLE_TYPES = {
    snow: {
        name: '雪花',
        color: 'rgba(255, 255, 255, 0.8)',
        sizeRange: [2, 5],
        speedRange: [1, 2],
        behavior: (particle) => {
            particle.stepSize = Math.random() / 15
            particle.step = Math.random() * 100
        },
        update: (particle) => {
            particle.velX += Math.cos((particle.step += 0.05)) * particle.stepSize
        },
        draw: (ctx, particle) => {
            ctx.beginPath()
            ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2)
            ctx.fill()
        }
    },
    rain: {
        name: '雨滴',
        color: 'rgba(150, 200, 255, 0.6)',
        sizeRange: [1, 3],
        speedRange: [2, 4],
        behavior: (particle) => {
            // 雨滴不需要额外行为
        },
        update: (particle) => {
            // 雨滴基本垂直下落
            particle.velX *= 0.9
        },
        draw: (ctx, particle) => {
            ctx.beginPath()
            ctx.moveTo(particle.x, particle.y)
            ctx.lineTo(particle.x - particle.size / 3, particle.y + particle.size * 2)
            ctx.lineWidth = 1
            ctx.stroke()
        }
    },
    leaves: {
        name: '落叶',
        color: 'rgba(200, 100, 50, 0.7)',
        sizeRange: [5, 12],
        speedRange: [0.5, 1.5],
        behavior: (particle) => {
            particle.rotation = Math.random() * Math.PI * 2
            particle.rotationSpeed = (Math.random() - 0.5) * 0.05
        },
        update: (particle) => {
            particle.rotation += particle.rotationSpeed
            particle.velX += (Math.random() - 0.5) * 0.2
        },
        draw: (ctx, particle) => {
            ctx.save()
            ctx.translate(particle.x, particle.y)
            ctx.rotate(particle.rotation)

            // 叶子形状
            ctx.beginPath()
            ctx.moveTo(0, 0)
            ctx.bezierCurveTo(
                particle.size,
                -particle.size / 2,
                particle.size / 2,
                -particle.size,
                0,
                -particle.size
            )
            ctx.bezierCurveTo(
                -particle.size / 2,
                -particle.size,
                -particle.size,
                -particle.size / 2,
                0,
                0
            )
            ctx.fill()

            // 叶脉
            ctx.beginPath()
            ctx.moveTo(0, 0)
            ctx.lineTo(0, -particle.size)
            ctx.strokeStyle = `rgba(0, 0, 0, ${particle.opacity * 0.5})`
            ctx.lineWidth = 0.5
            ctx.stroke()

            ctx.restore()
        }
    },
    dandelion: {
        name: '蒲公英',
        color: 'rgba(255, 255, 255, 0.7)',
        sizeRange: [3, 8],
        speedRange: [0.3, 1.2],
        behavior: (particle) => {
            particle.rotation = Math.random() * Math.PI * 2
            particle.rotationSpeed = (Math.random() - 0.5) * 0.02
            particle.velX = (Math.random() - 0.5) * 0.5
        },
        update: (particle) => {
            particle.rotation += particle.rotationSpeed
            particle.velX += (Math.random() - 0.5) * 0.1
            particle.velY += (Math.random() - 0.5) * 0.05
        },
        draw: (ctx, particle) => {
            ctx.save()
            ctx.translate(particle.x, particle.y)

            // 种子中心
            ctx.beginPath()
            ctx.arc(0, 0, particle.size / 2, 0, Math.PI * 2)
            ctx.fillStyle = `rgba(255, 255, 255, ${particle.opacity})`
            ctx.fill()

            // 绒毛
            const lines = 8
            for (let i = 0; i < lines; i++) {
                const angle = (i / lines) * Math.PI * 2
                ctx.beginPath()
                ctx.moveTo(0, 0)
                ctx.lineTo(
                    Math.cos(angle) * particle.size * 1.5,
                    Math.sin(angle) * particle.size * 1.5
                )
                ctx.strokeStyle = `rgba(255, 255, 255, ${particle.opacity * 0.7})`
                ctx.lineWidth = 0.8
                ctx.stroke()
            }

            ctx.restore()
        }
    }
}
// 组件属性定义
const props = defineProps({
    // 粒子效果切换间隔(毫秒)
    changeInterval: {
        type: Number,
        default: 15000, // 默认15秒
        validator: (value) => value >= 5000 // 验证器：不小于5秒
    },
    // 粒子数量
    particleCount: {
        type: Number,
        default: 150,
        validator: (value) => value > 0 && value <= 1000 // 验证器：1-1000之间
    },
    // 是否启用鼠标交互(粒子避让鼠标)
    mouseInteraction: {
        type: Boolean,
        default: true
    }
})

// 定义组件事件
const emit = defineEmits(['particleTypeChange'])

// 响应式引用
const canvas = ref(null) // 画布DOM引用
const ctx = ref(null) // 画布2D上下文
const particles = ref([]) // 存储所有粒子对象的数组
const mouseX = ref(-100) // 鼠标X坐标
const mouseY = ref(-100) // 鼠标Y坐标
const animationFrameId = ref(null) // 动画帧ID(用于取消动画)
const resizeObserver = ref(null) // 用于监听尺寸变化的观察者
const changeTimer = ref(null) // 粒子切换计时器
const currentType = ref('rain') // 当前粒子类型

/**
 * 获取随机粒子类型
 * @returns {string} 随机粒子类型key
 */
function getRandomParticleType() {
    const types = Object.keys(PARTICLE_TYPES)
    let newType
    // 确保新类型与当前类型不同(当有多种类型时)
    do {
        newType = types[Math.floor(Math.random() * types.length)]
    } while (newType === currentType.value && types.length > 1)
    return newType
}

/**
 * 创建单个粒子对象
 * @returns {object} 粒子对象
 */
function createParticle() {
    const config = PARTICLE_TYPES[currentType.value]
    const [minSize, maxSize] = config.sizeRange
    const [minSpeed, maxSpeed] = config.speedRange

    // 粒子基础属性
    const particle = {
        x: Math.random() * canvas.value.width, // 随机x位置
        y: Math.random() * canvas.value.height, // 随机y位置
        size: Math.random() * (maxSize - minSize) + minSize, // 随机大小
        speed: Math.random() * (maxSpeed - minSpeed) + minSpeed, // 随机速度
        velY: Math.random() * (maxSpeed - minSpeed) + minSpeed, // y轴速度
        velX: 0, // x轴速度初始为0
        opacity: Math.random() * 0.5 + 0.3 // 随机透明度(0.3-0.8)
    }

    // 调用特定类型的初始化行为
    config.behavior(particle)
    return particle
}

/**
 * 重置粒子位置和属性
 * @param {object} particle 要重置的粒子对象
 */
function resetParticle(particle) {
    const config = PARTICLE_TYPES[currentType.value]
    const [minSize, maxSize] = config.sizeRange
    const [minSpeed, maxSpeed] = config.speedRange

    // 重置到画布顶部外
    particle.x = Math.random() * canvas.value.width
    particle.y = -maxSize * 2
    // 重置大小、速度等属性
    particle.size = Math.random() * (maxSize - minSize) + minSize
    particle.speed = Math.random() * (maxSpeed - minSpeed) + minSpeed
    particle.velY = particle.speed
    particle.velX = 0
    particle.opacity = Math.random() * 0.5 + 0.3

    // 重新初始化行为
    config.behavior(particle)
}

/**
 * 更新粒子位置
 * @param {object} particle 要更新的粒子对象
 */
function updateParticlePosition(particle) {
    const config = PARTICLE_TYPES[currentType.value]

    // 鼠标避让效果
    if (props.mouseInteraction) {
        const dx = particle.x - mouseX.value
        const dy = particle.y - mouseY.value
        const dist = Math.sqrt(dx * dx + dy * dy)

        // 当粒子接近鼠标时产生排斥力
        if (dist < 350) {
            const force = 350 / (dist * dist) // 力的大小与距离平方成反比
            const xcomp = (mouseX.value - particle.x) / dist // x方向分量
            const ycomp = (mouseY.value - particle.y) / dist // y方向分量
            const deltaV = force / 2 // 速度变化量

            // 更新速度(远离鼠标)
            particle.velX -= deltaV * xcomp
            particle.velY -= deltaV * ycomp
        }
    }

    // 通用物理效果
    particle.velX *= 0.98 // 空气阻力
    // 确保最小下落速度
    if (particle.velY <= particle.speed) {
        particle.velY = particle.speed
    }

    // 调用特定类型的更新行为
    config.update(particle)

    // 更新位置
    particle.y += particle.velY
    particle.x += particle.velX

    // 边界检查 - 超出边界则重置粒子
    if (particle.y >= canvas.value.height + particle.size * 2 || particle.y <= -particle.size * 2) {
        resetParticle(particle)
    }
    if (particle.x >= canvas.value.width + particle.size * 2 || particle.x <= -particle.size * 2) {
        resetParticle(particle)
    }
}

/**
 * 动画循环函数
 */
function animate() {
    if (!ctx.value) return

    const config = PARTICLE_TYPES[currentType.value]

    // 设置绘制样式
    if (currentType.value === 'rain') {
        // 雨滴使用描边样式
        ctx.value.strokeStyle = config.color
        ctx.value.lineWidth = 1
    } else {
        // 其他粒子使用填充样式
        ctx.value.fillStyle = config.color
    }

    // 清空画布
    ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height)

    // 更新并绘制所有粒子
    particles.value.forEach((particle) => {
        updateParticlePosition(particle)
        config.draw(ctx.value, particle)
    })

    // 请求下一帧动画
    animationFrameId.value = requestAnimationFrame(animate)
}

/**
 * 切换粒子类型
 */
function changeParticleType() {
    // 获取新类型
    currentType.value = getRandomParticleType()
    // 触发事件通知父组件
    emit('particleTypeChange', currentType.value)

    // 重置所有粒子
    particles.value = Array.from({ length: props.particleCount }, createParticle)

    // 设置下一次切换
    changeTimer.value = setTimeout(changeParticleType, props.changeInterval)
}

/**
 * 处理鼠标移动事件
 * @param {MouseEvent} e 鼠标事件对象
 */
function handleMouseMove(e) {
    if (!props.mouseInteraction) return
    mouseX.value = e.clientX
    mouseY.value = e.clientY
}

/**
 * 处理窗口大小变化
 */
function handleResize() {
    canvas.value.width = window.innerWidth
    canvas.value.height = window.innerHeight
}

/**
 * 初始化粒子系统
 */
function init() {
    if (!canvas.value) return

    // 获取2D上下文
    ctx.value = canvas.value.getContext('2d')
    // 设置画布尺寸
    canvas.value.width = window.innerWidth
    canvas.value.height = window.innerHeight

    // 创建初始粒子
    particles.value = Array.from({ length: props.particleCount }, createParticle)

    // 开始动画循环
    animate()

    // 开始自动切换粒子类型
    changeTimer.value = setTimeout(changeParticleType, props.changeInterval)
}

// 监听粒子数量变化
watch(
    () => props.particleCount,
    (newVal, oldVal) => {
        if (newVal > oldVal) {
            // 增加粒子 - 创建新粒子
            const newParticles = Array.from({ length: newVal - oldVal }, createParticle)
            particles.value.push(...newParticles)
        } else if (newVal < oldVal) {
            // 减少粒子 - 截断数组
            particles.value.length = newVal
        }
    }
)

// 组件挂载时初始化
onMounted(() => {
    init()

    // 注册鼠标移动事件
    if (props.mouseInteraction) {
        canvas.value.addEventListener('mousemove', handleMouseMove)
    }

    // 使用ResizeObserver监听尺寸变化(比resize事件更精确)
    resizeObserver.value = new ResizeObserver(() => {
        handleResize()
    })
    resizeObserver.value.observe(canvas.value)
})

// 组件卸载前清理
onBeforeUnmount(() => {
    // 取消动画帧
    if (animationFrameId.value) {
        cancelAnimationFrame(animationFrameId.value)
    }

    // 清除切换计时器
    if (changeTimer.value) {
        clearTimeout(changeTimer.value)
    }

    // 移除事件监听
    if (props.mouseInteraction) {
        canvas.value.removeEventListener('mousemove', handleMouseMove)
    }

    // 断开尺寸观察
    if (resizeObserver.value) {
        resizeObserver.value.disconnect()
    }
})
</script>

<style scoped>
/* 画布样式 - 固定定位覆盖整个视口 */
.particles-canvas {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 99999;
    /* 允许鼠标事件穿透 */
    pointer-events: none;
}
</style>
